﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Data.GameData;
using Data.GameData.Interfaces;
using GUIFramework;

namespace GUIComponents
{
    public partial class SystemListToolbox : DockableToolWindow
    {
        #region SystemTreeNode
        internal class SystemTreeNode : TreeNode
        {
            private Guid raceId;
            private Guid sysInfoId;
            private int mapId;

            public SystemTreeNode()
            {
                raceId = Guid.Empty;
                sysInfoId = Guid.Empty;
                mapId = -1;
            }

            public Guid RaceId
            {
                get { return raceId; }
                set { raceId = value; }
            }
            public Guid SystemInfoId
            {
                get { return sysInfoId; }
                set { sysInfoId = value; }
            }
            public int MapId
            {
                get { return mapId; }
                set { mapId = value; }
            }
        }
        #endregion
        
        /// <summary>
        /// Constructor
        /// </summary>
        public SystemListToolbox()
        {
            InitializeComponent();
            PanelType = GamePanelType.pSYSTEMLIST;
        }

        /// <summary>
        /// Build from scratch the entire system tree
        /// </summary>
        private void BuildSystemTree()
        {
            systemTreeView.SuspendLayout();
            systemTreeView.Nodes.Clear();

            // List the owned systems
            SystemTreeNode activeRaceNode = new SystemTreeNode { Text= Race.ActiveRace.Name, RaceId = Race.ActiveRace.Id };
            systemTreeView.Nodes.Add(activeRaceNode);
            foreach (StarSystemInformation system in Race.ActiveRace.OwnedStarSystems.OrderBy(sys => sys.Name))
            {
                SystemTreeNode node = new SystemTreeNode { Text = system.Name, RaceId = system.OwnerId, SystemInfoId = system.Id, MapId = system.MapId };
                activeRaceNode.Nodes.Add(node);
            }

            // List other systems
            SystemTreeNode otherNode = new SystemTreeNode { Text = "Other" };
            systemTreeView.Nodes.Add(otherNode);
            foreach (StarSystemInformation system in Race.ActiveRace.KnownStarSystems.Except(Race.ActiveRace.OwnedStarSystems).OrderBy(sys => sys.Name))
            {
                SystemTreeNode node = new SystemTreeNode { Text = system.Name, RaceId = system.OwnerId, SystemInfoId = system.Id, MapId = system.MapId };
                otherNode.Nodes.Add(node);
            }

            systemTreeView.ResumeLayout();
        }

        /// <summary>
        /// Update the system tree by either adding, deleting or modifying a node.
        /// </summary>
        /// <param name="systemId">The id of the system information</param>
        /// <param name="operation">The type of operation</param>
        private void UpdateSystemTree(BasicEventArgs e)
        {
            if (e.EventType == BasicEventType.eRACE && e.Operation == BasicOperationType.eMODIFY)
            {
                // Determine if the active race knows about the updated system
                Race race = Race.GetRace(e.Ident);
                if (race == null)
                    return;

                systemTreeView.SuspendLayout();

                if (e.Operation == BasicOperationType.eMODIFY)
                {
                    SystemTreeNode systemNode = LocateNode(race.Id, systemTreeView.Nodes);
                    if (systemNode != null)
                    {
                        systemNode.Text = race.Name;
                    }
                }

                systemTreeView.ResumeLayout();
                return;
            }

            // Determine if the active race knows about the updated system
            StarSystemInformation info = Race.ActiveRace.KnownStarSystems.FirstOrDefault(sys => sys.Id == e.Ident);
            if ( info == null)
                return;

            systemTreeView.SuspendLayout();

            if (e.Operation == BasicOperationType.eMODIFY)
            {
                SystemTreeNode systemNode = LocateNode(info.Id, systemTreeView.Nodes);
                if (systemNode != null)
                {
                    systemNode.Text = info.Name;
                }
            }

            if (e.Operation == BasicOperationType.eADD)
            {
                if (info.OwnerId != Guid.Empty)
                {
                    SystemTreeNode systemNode = LocateNode(info.OwnerId, systemTreeView.Nodes);
                    if (systemNode != null)
                    {
                        SystemTreeNode sys = new SystemTreeNode { Text = info.Name, RaceId = info.OwnerId, SystemInfoId = info.Id, MapId = info.MapId };
                        systemNode.Parent.Nodes.Add(sys);
                    }
                }
                else
                {
                    SystemTreeNode systemNode = LocateNode(Guid.Empty, systemTreeView.Nodes);
                    if (systemNode != null)
                    {
                        SystemTreeNode sys = new SystemTreeNode { SystemInfoId = info.Id, MapId = info.MapId };
                        systemNode.Parent.Nodes.Add(info.Name);
                    }
                }
            }

            if (e.Operation == BasicOperationType.eDELETE)
            {
                SystemTreeNode systemNode = LocateNode(info.Id, systemTreeView.Nodes);
                if (systemNode != null)
                {
                    systemNode.Parent.Nodes.Remove(systemNode);
                }
            }

            systemTreeView.ResumeLayout();
        }

        /// <summary>
        /// Recursively search for a node containing a specific id.
        /// </summary>
        /// <param name="idToLocate">The guid id to find</param>
        /// <param name="nodes">The collection of nodes</param>
        /// <returns>The result of the search (can be null)</returns>
        private SystemTreeNode LocateNode(Guid idToLocate, TreeNodeCollection nodes)
        {
            foreach (SystemTreeNode node in nodes)
            {
                if (node.SystemInfoId == idToLocate || node.RaceId == idToLocate)
                    return node;

                SystemTreeNode childNode = LocateNode(idToLocate, node.Nodes);
                if (childNode != null)
                    return childNode;
            }

            return null;
        }

        /// <summary>
        /// Ensure the tree is built as the panel is constructed.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        private void SystemListToolbox_Load(object sender, EventArgs e)
        {
            BuildSystemTree();
        }

        private void newSectorToolStripButton_Click(object sender, EventArgs e)
        {

        }

        private void deleteSectorToolStripButton_Click(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Trigger an update the panel.
        /// </summary>
        /// <param name="e">The game event arguments</param>
        public override void UpdatePanel(BasicEventArgs e)
        {
            if (e == null)
                return;

            if (e.EventType == BasicEventType.eRACE)
            {
                if (e.Operation == BasicOperationType.eMODIFY)
                    UpdateSystemTree(e);
                else
                    BuildSystemTree();
            }
            else if (e.EventType == BasicEventType.eSTAR_SYSTEM)
                UpdateSystemTree(e);
        }

        /// <summary>
        /// Enable/Disable the menus based on the selected tree node.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        private void systemTreeContextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            SystemTreeNode selected = systemTreeView.SelectedNode as SystemTreeNode;
            showMapToolStripMenuItem.Enabled = selected.MapId != -1;
        }

        /// <summary>
        /// Open a map view based on the selected node.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        private void showMapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SystemTreeNode selected = systemTreeView.SelectedNode as SystemTreeNode;
            if (selected == null || selected.MapId == -1)
                return;

            // Force the starsystem to load completely before showing the map.
            StarSystemInformation info = StarSystemInformation.GetStarSystemInfo(selected.SystemInfoId);
            StarSystem sys = StarSystem.GetStarSystem(info.SystemId);

            Commands.OpenView(selected.MapId, ViewType.vtMAP_STRATEGIC);
        }

        /// <summary>
        /// Open a properties dialog based on the selected node.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        private void propertiesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SystemTreeNode selected = systemTreeView.SelectedNode as SystemTreeNode;
            if (selected == null)
                return;

            if (selected.MapId == -1 && selected.RaceId != Guid.Empty)
            {
                Commands.ShowRaceProperties(Race.GetRace(selected.RaceId));
            }
            else if( selected.MapId >= 0 )
            {
                Commands.ShowSystemProperties(selected.SystemInfoId);
            }
        }

        /// <summary>
        /// Select the tree node under the as the mouse is clicked.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">The mouse arguments</param>
        private void systemTreeView_MouseDown(object sender, MouseEventArgs e)
        {
            // Change the right-click to select the node clicked on
            if (e.Button == MouseButtons.Right)
            {
                // Point where the mouse is clicked.
                Point p = new Point(e.X, e.Y);

                // Get the node that the user has clicked.
                TreeNode node = systemTreeView.GetNodeAt(p);
                if (node != null)
                    systemTreeView.SelectedNode = node;
            }
        }
    }
}
